package com.example.demo.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocatorMetric;

public class NettyByteBuf {
    public static void main(String[] args) {
        // 在netty开辟的堆内存储池中划定一个1024大小的存储空间
        ByteBuf byteBuf1 = PooledByteBufAllocator.DEFAULT.heapBuffer(1024);
        // 在netty开辟的堆外存储池中划定一个1024大小的存储空间
        ByteBuf byteBuf2 = PooledByteBufAllocator.DEFAULT.directBuffer(1024);

        // 写入字节数组
        byteBuf1.writeBytes("qwe".getBytes());
        // 设置从哪个下标开始写入一些数据,注意它不会改变写入的指针位置
        // 因为没有合适定位所以需要额外用两个指针去定位它
        // 主要是在需要修改数据而不改变指针的时候使用它
        byteBuf1.setBytes(4,"cc".getBytes());
        // 输出当前写入指针的下标位置，显然这里会是3（0,1,2已用）
        // 也可以传入int参数从而修改当前下标位置
        System.out.println(byteBuf1.writerIndex());

        // 注意这里用get操作获取元素不会改变读取下标位置
        System.out.println((char)byteBuf1.getByte(1));
        // 输出当前读取指针的下标位置（这里会是0,因为get操作是不会改变下标位置的！）
        // 同样可以传入int参数从而修改当前下标位置
        System.out.println(byteBuf1.readerIndex());

        // 这个read操作会改变读取下标位置
        System.out.println((char)byteBuf1.readByte());
        // 所以这里会输出1
        System.out.println(byteBuf1.readerIndex());
        // 这样会输出基本信息,数据类型、写入和读取的指针位置和数组容量
        System.out.println(byteBuf1);
        // 转字符串之类的也都可以
        System.out.println(new String(byteBuf1.array(), byteBuf1.readerIndex(), byteBuf1.writerIndex()));

        // 设置数组容量
        byteBuf1.capacity(2048);

        // 表示从当前可读位置开始后面还有几个元素可读,这个会返回2（当前位置是w还剩下we可读)
        System.out.println(byteBuf1.readableBytes());
        // 表示从当前可写位置开始后面还有几个元素可写,这个会返回2045（因为上面重置了容量为2048）
        System.out.println(byteBuf1.writableBytes());

        // 丢弃已读部分的元素,并将后面元素往前补位(相当于逻辑删除已读的,然后读写两个指针左移对应的位数)
        // 注意它不会真的删除元素,感觉唯一的用处就是读完之后不用清空数据而是直接下次写入时覆盖
        // 所以千万别在还没读取完成的时候使用它,会导致数据错乱的
        byteBuf1.discardReadBytes();
        // 所以这里的可读指针会是0,可写指针会是2
        System.out.println(byteBuf1);

        // 查看这个对象引用计数，这里会是1
        // 比如其他地方也正在使用它时,如果你在这里把它释放掉了,其他地方就会指空
        System.out.println(byteBuf1.refCnt());

        // 这里假装将这个byteBuf1的数据传入了其他方法中引用
        qwe(byteBuf1);
        // 这里能打印出来东西,说明内存还没有被释放
        System.out.println(byteBuf1);

        // 释放内存,这个很重要,如果没有释放它会永远被标记占用,然后等到程序结束后它就相当于内存泄露了
        // 举个例子，不释放然后每次请求都创建一个,就会不停开辟内存,直到把服务器内存挤爆
        // 如果它这里发现自己已经是最后一个使用的人了,那么就会将计数-1后直接释放掉内存
        byteBuf1.release();
        // 这里就会输出0了,后面再想使用byteBuf1就会因为指空报错了
        System.out.println(byteBuf1.refCnt());

        // 这里补充一个读取ByteBuf的方式,直接给byte数组填充数据
        // 会从byteBuf2中读取bytes大小的数据并填充进bytes,实际根据需要调整大小
        byte[] bytes = new byte[5];
        byteBuf2.readBytes(bytes);
        // 其他的也别忘了释放
        byteBuf2.refCnt();

    }
    public static void qwe(ByteBuf qwe){
        // 增加一个计数,表示它正在用这块内存的数据
        qwe.retain();
        // 输出计数,这里会是2
        System.out.println(qwe.refCnt());
        // 减少一次计数,因为这里-1后是1,所以不会释放内存
        qwe.release();
    }
}
